// -*- mode:c++ -*-

// Copyright (c) 2015 Riscv Developers
// Copyright (c) 2016-2017 The University of Virginia
// Copyright (c) 2020 Barkhausen Institut
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

////////////////////////////////////////////////////////////////////
//
// Floating point operation instructions
//
def template FloatExecute {{
    Fault %(class_name)s::execute(ExecContext *xc,
        Trace::InstRecord *traceData) const
    {
        STATUS status = xc->readMiscReg(MISCREG_STATUS);
        if (status.fs == FPUStatus::OFF)
            return std::make_shared<IllegalInstFault>("FPU is off", machInst);

        %(op_decl)s;
        %(op_rd)s;

        RegVal FFLAGS = xc->readMiscReg(MISCREG_FFLAGS);
        std::feclearexcept(FE_ALL_EXCEPT);
        %(code)s;

        FFLAGS |= softfloat_exceptionFlags;
        if (softfloat_exceptionFlags) {
            softfloat_exceptionFlags = 0;
            xc->setMiscReg(MISCREG_FFLAGS, FFLAGS);
        }

        %(op_wb)s;

        return NoFault;
    }
}};

def template FMulExecute {{
    Fault %(class_name)s::%(class_name)sMul::execute(ExecContext *xc,
        Trace::InstRecord *traceData) const
    {
        return NoFault;
    }
}};

def template FAccExecute {{
    Fault %(class_name)s::%(class_name)sAcc::execute(ExecContext *xc,
        Trace::InstRecord *traceData) const
    {
        STATUS status = xc->readMiscReg(MISCREG_STATUS);
        if (status.fs == FPUStatus::OFF)
            return std::make_shared<IllegalInstFault>("FPU is off", machInst);

        %(op_decl)s;
        %(op_rd)s;

        RegVal FFLAGS = xc->readMiscReg(MISCREG_FFLAGS);
        std::feclearexcept(FE_ALL_EXCEPT);
        %(code)s;

        FFLAGS |= softfloat_exceptionFlags;
        if (softfloat_exceptionFlags) {
            softfloat_exceptionFlags = 0;
            xc->setMiscReg(MISCREG_FFLAGS, FFLAGS);
        }

        %(op_wb)s;

        return NoFault;
    }
}};

def format FPROp(code, *opt_flags) {{
    iop = InstObjParams(name, Name, 'RegOp', code, opt_flags)
    header_output = BasicDeclare.subst(iop)
    decoder_output = BasicConstructor.subst(iop)
    decode_block = BasicDecode.subst(iop)
    exec_output = FloatExecute.subst(iop)
}};

def template FMAccOpMacroConstructor {{
    %(class_name)s::%(class_name)s(MachInst machInst)
            : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
    {
        %(constructor)s;

        StaticInstPtr mul_op;
        StaticInstPtr acc_op;

        mul_op = new %(class_name)sMul(machInst, this);
        mul_op->setFlag(IsFirstMicroop);

        acc_op = new %(class_name)sAcc(machInst, this);
        acc_op->setFlag(IsLastMicroop);

        microops = {mul_op, acc_op};
    }
}};

def template FMADeclare {{
    /**
     * Static instruction class for an FMA operation
     */
    class %(class_name)s : public %(base_class)s
    {
      public:
        // Constructor
        %(class_name)s(MachInst machInst);

      protected:
        /*
         * The uop of Mul and Acc
         */
        class %(class_name)sMul;

        class %(class_name)sAcc;
    };
}};

def template FMulOpDeclare {{
    /*
     * The Multiplication part of an FMAcc
     */
    class %(class_name)s::%(class_name)sMul : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        // Constructor
        %(class_name)sMul(MachInst machInst, %(class_name)s *_p);

        Fault execute(ExecContext *, Trace::InstRecord *) const override;
    };
}};

def template FAccOpDeclare {{
    /*
     * The Acc part of an FMAcc
     */
    class %(class_name)s::%(class_name)sAcc : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        // Constructor
        %(class_name)sAcc(MachInst machInst, %(class_name)s *_p);

        Fault execute(ExecContext *, Trace::InstRecord *) const override;
    };
}};

def template FMulOpConstructor {{
    %(class_name)s::%(class_name)sMul::%(class_name)sMul(
        MachInst machInst, %(class_name)s *_p)
            : %(base_class)s("%(mnemonic)s[Mul]", machInst, FMAMulOp)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
    }
}};

def template FAccOpConstructor {{
    %(class_name)s::%(class_name)sAcc::%(class_name)sAcc(
        MachInst machInst, %(class_name)s *_p)
            : %(base_class)s("%(mnemonic)s[Acc]", machInst, FMAAccOp)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
    }
}};

def format FMAccOp(mul_code, acc_code, *opt_flags) {{
    iop = InstObjParams(name, Name, 'FMAMOp', '', opt_flags)
    header_output = FMADeclare.subst(iop)
    decoder_output = FMAccOpMacroConstructor.subst(iop)
    decode_block = BasicDecode.subst(iop)
    exec_output = ''

    mul_iop = InstObjParams(name, Name, 'FMAUOp',
                            mul_code, opt_flags)

    header_output += FMulOpDeclare.subst(mul_iop)
    decoder_output += FMulOpConstructor.subst(mul_iop)
    exec_output += FMulExecute.subst(mul_iop)

    add_iop = InstObjParams(name, Name, 'FMAUOp',
                            acc_code, opt_flags)
    header_output += FAccOpDeclare.subst(add_iop)
    decoder_output += FAccOpConstructor.subst(add_iop)
    exec_output += FAccExecute.subst(add_iop)
}};
